function gewAnimatedMeshNode(name,isSmoothInterpolate)
{ 
  gewNode.call(this,name);
  // PRIVATE:
  this.mesh = null;  
  if(isSmoothInterpolate==true)
    this.isSmoothInterpolate = true;
  else
    this.isSmoothInterpolate = false; 
  this.animationIdx = 0;
  this.animationState = GEW_STOP;
  this.animationSpeed = 1.0;
  this.isLoop = false;
  //this.startTime = 0;
  this.previousFrameTime = 0;
  this.ellapseTime = 0;
  this.boneMatBuf = null;// use for cache current frame's bone mat
}
extend2(gewAnimatedMeshNode, gewNode);
gewAnimatedMeshNode.prototype.setAnimation = function(idx)
{
  this.animationIdx = idx;
  this.localAabb = this.mesh.aabbs[idx];
  this.localSphere = this.mesh.spheres[idx];
  this.previousFrameTime = this.scene3d.game.time;
  this.ellapseTime = 0;
  this.setFlag(NODE_UPDATE_TRANSLATE_MAT,true);// to force this node update world bounding volume; 
}
gewAnimatedMeshNode.prototype.setFrame = function(frame)
{
  var mlsPerFrame = this.mesh.mlsPerFrame;  
  this.ellapseTime = mlsPerFrame*frame;
  this.previousFrameTime = this.scene3d.game.time;
}
gewAnimatedMeshNode.prototype.play = function(isLoop)
{
  if(isLoop!=undefined)
  {
    this.isLoop = isLoop;
  }
  if(this.animationState== GEW_STOP)
  {
    this.ellapseTime =0;
    this.previousFrameTime = this.scene3d.game.time;
    this.animationState = GEW_PLAYING;
  }
  else if( this.animationState== GEW_PAUSE)
  {
    this.previousFrameTime = this.scene3d.game.time;
    this.animationState = GEW_PLAYING;
  }
}
gewAnimatedMeshNode.prototype.stop = function()
{
  this.ellapseTime = 0;
  this.animationState = GEW_STOP;
}
gewAnimatedMeshNode.prototype.pause = function()
{
  if(this.animationState==GEW_PLAYING)
  {
    this.animationState = GEW_PAUSE;
  }
}
gewAnimatedMeshNode.prototype.setMesh = function(mesh)
{
  if(mesh.meshType==GEW_SKINNED_MESH&& this.isSmoothInterpolate)
  {
    var boneNum = mesh.boneNumber;
    if(this.boneMatBuf)
      delete this.boneMatBuf;
    this.boneMatBuf = new WebGLFloatArray(boneNum*16);
  }
  this.mesh = mesh;
}
gewAnimatedMeshNode.prototype.dup = function()
{
  var copy = new gewAnimatedMeshNode(this.name+"_"+getUniqueNumber());
  copy.copyFrom(this);
  copy.setMesh(this.mesh);
  return copy;
}
gewAnimatedMeshNode.prototype.getMesh = function()
{
  return this.mesh;
}

gewAnimatedMeshNode.prototype.setAttributePointers = function(material)
{
  var m = this.mesh; 
  var effect = material.effect;
  var gl = effect.gl;
  switch (effect.name) {
    case "PhongSkinnedEffect":
      var shader = effect.shader;
      gl.bindBuffer(gl.ARRAY_BUFFER, m.glBuffers.positions);
      gl.vertexAttribPointer(shader.inPosition, 3, gl.FLOAT, false, 0, 0);
      gl.bindBuffer(gl.ARRAY_BUFFER, m.glBuffers.texcoords);
      gl.vertexAttribPointer(shader.inTexCoord0, 2, gl.FLOAT, false, 0, 0);
      gl.bindBuffer(gl.ARRAY_BUFFER, m.glBuffers.normals);
      gl.vertexAttribPointer(shader.inNormal, 3, gl.FLOAT, false, 0, 0); 
      
      gl.bindBuffer(gl.ARRAY_BUFFER, m.glBuffers.boneIdx);
      gl.vertexAttribPointer(shader.inMatIdx, 4, gl.FLOAT, false, 0, 0);
      gl.bindBuffer(gl.ARRAY_BUFFER, m.glBuffers.weight);
      gl.vertexAttribPointer(shader.inWeight, 4, gl.FLOAT, false, 0, 0);
      break;
    case "CSMEffect":
      var shader = effect.shaders.skinnedMesh;
      gl.bindBuffer(gl.ARRAY_BUFFER, m.glBuffers.positions);
      gl.vertexAttribPointer(shader.inPosition, 3, gl.FLOAT, false, 0, 0);        
      gl.bindBuffer(gl.ARRAY_BUFFER, m.glBuffers.boneIdx);
      gl.vertexAttribPointer(shader.inMatIdx, 4, gl.FLOAT, false, 0, 0);
      gl.bindBuffer(gl.ARRAY_BUFFER, m.glBuffers.weight);
      gl.vertexAttribPointer(shader.inWeight, 4, gl.FLOAT, false, 0, 0);  
      break;
  }  
}
gewAnimatedMeshNode.prototype.render = function(material)
{
  var m = this.mesh; 
  var effect = material.effect;
  var gl = effect.gl;
  if(!m|| !m.isReady)
    return;     
  if(m.meshType==GEW_SKINNED_MESH)
  {        
    // Calc bone matrix at current frame
    var time = this.scene3d.game.time;
    if(this.previousFrameTime!=time) // Should updated bone matrix
    {
      var spd = this.animationSpeed;
      var boneNum = m.boneNumber;
      var matBuf = this.boneMatBuf;
      var aniIdx = this.animationIdx;
      var dur = m.durations[aniIdx];
      var state = this.animationState; 
      var mlsPerFrame = m.mlsPerFrame;        
      var ellapseTime = this.ellapseTime;  
      if (state == GEW_PLAYING) {
        ellapseTime+= (time - this.previousFrameTime)*spd;
        if (ellapseTime > dur) {
          if (!this.isLoop) {
            ellapseTime = dur;
            this.stop();
          }
          else {
            var f = Math.floor(ellapseTime/dur);
            ellapseTime = ellapseTime- f*dur;
          }
        }
        this.ellapseTime = ellapseTime;
        this.previousFrameTime = time;
      }
      // Get mat 
      var offset = ellapseTime/mlsPerFrame;
      var round_offset = Math.round(offset);
      var deformMats = m.animationMats;      
      var boneMats = m.animationBoneMats;
      
      if (this.isSmoothInterpolate) {
        if (gewIsEqual(offset, round_offset, 0.01)) {
          var buff = boneMats[aniIdx][round_offset];
          matBuf.set(buff);
        }
        else {
          var frame = Math.floor(offset);
          var frame_offset = frame * boneNum;
          var next_frame_offset = frame_offset + boneNum;
          var factor = (ellapseTime - (frame * mlsPerFrame)) / mlsPerFrame;
          
          var buf1 = boneMats[aniIdx][frame];
          var buf2 = boneMats[aniIdx][frame + 1];
          
          var count = boneNum * 16;
          for (var i = 0; i < count; i++) {
            matBuf[i] = gewMix(buf1[i], buf2[i], factor);
          }
        }
      }
      else
      {
        this.boneMatBuf = boneMats[aniIdx][round_offset];
      }
    }
    if (effect.name == "PhongSkinnedEffect") {
      gl.uniformMatrix4fv(effect.shader.boneMat, false, this.boneMatBuf);
    }
    else if (effect.name == "CSMEffect") {
      var shader = effect.shaders.skinnedMesh;
      gl.uniformMatrix4fv(shader.boneMat, false, this.boneMatBuf);
    }
    //Draw
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, m.glBuffers.indices[0]);
    gl.drawElements(gl.TRIANGLES,m.indices[0].length,gl.UNSIGNED_SHORT,0);
  }
}

